/*
 * Copyright (C) 2009 Georgios J. Georgopoulos, All rights reserved.
 * 
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 * 
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.gwt.mosaic.xul.client.ui;

import java.util.ArrayList;
import java.util.List;

import org.gwt.mosaic.forms.client.layout.CellConstraints;
import org.gwt.mosaic.forms.client.layout.FormLayout;
import org.gwt.mosaic.ui.client.layout.LayoutPanel;

import com.google.gwt.user.client.ui.Widget;

/**
 * A grid is a layout type that arranges elements in rows and columns. The grid
 * is expected to contain both a columns element as a child which defines the
 * structure of the columns and a rows element as a child to define the rows.
 * TODO ...
 * 
 * @author georgopoulos.georgios(at)gmail.com
 * 
 */
public class Grid extends Container {
  private static final long serialVersionUID = -839264611578801794L;

  private transient LayoutPanel layoutPanel;

  private Columns columns;
  private Rows rows;

  public Grid() {
    super();
  }

  @Override
  public Element add(Element element, int index) {
    if (element instanceof Columns) {
      if (this.columns != null) {
        remove(this.columns);
      }
      this.columns = (Columns) element;
      return super.add(element, index);
    } else if (element instanceof Rows) {
      if (this.rows != null) {
        remove(this.rows);
      }
      this.rows = (Rows) element;
      return super.add(element, index);
    }
    return null;
  }

  @Override
  public Element remove(int index) {
    Element element = elements.get(index);
    if (element instanceof Columns) {
      this.columns = null;
      return elements.remove(index);
    } else if (element instanceof Rows) {
      this.rows = null;
      return elements.remove(index);
    }
    return null;
  }

  @Override
  protected Widget createUI() {
    if (layoutPanel == null) {
      layoutPanel = new LayoutPanel();
      layoutPanel.setStyleName("xul-Grid");
    }

    int hFlex = calculateHFlex();
    int vFlex = calculateVFlex();

    StringBuffer columnsSB = new StringBuffer();

    int cols = 0;
    if (columns != null) {
      for (int i = 0, n = columns.getElementCount(); i < n; i++, cols++) {
        Column column = (Column) columns.getElement(i);
        int flex = 0;
        if (column.getAttributeMap().containsKey(Element.FLEX)) {
          flex += column.getFlex();
        }

        String align = column.getAlign();
        if ("start".equals(align)) {
          align = "left";
        } else if ("center".equals(align)) {
          align = "center";
        } else if ("end".equals(align)) {
          align = "right";
        } else {
          align = "fill";
        }

        if (i + 1 < n) {
          if (flex > 0) {
            columnsSB.append(align + ":pref:grow("
                + ((double) flex / (double) hFlex) + "), " + HDLU + "dlu, ");
          } else {
            columnsSB.append(align + ":pref, " + HDLU + "dlu, ");
          }
        } else {
          if (flex > 0) {
            columnsSB.append(align + ":pref:grow("
                + ((double) flex / (double) hFlex) + ")");
          } else {
            columnsSB.append(align + ":pref");
          }
        }
      }
    }

    StringBuffer rowsSB = new StringBuffer();

    if (rows != null) {
      for (int i = 0, n = rows.getElementCount(); i < n; i++) {
        final Row row = (Row) rows.getElement(i);

        int flex = 0;
        if (row.getAttributeMap().containsKey(Element.FLEX)) {
          flex += row.getFlex();
        }

        String align = row.getAlign();
        if ("start".equals(align)) {
          align = "top";
        } else if ("center".equals(align)) {
          align = "center";
        } else if ("end".equals(align)) {
          align = "bottom";
        } else {
          align = "fill";
        }

        if (i + 1 < n) {
          if (flex > 0) {
            rowsSB.append(align + ":pref:grow("
                + ((double) flex / (double) vFlex) + "), " + VDLU + "dlu, ");
          } else {
            rowsSB.append(align + ":pref, " + VDLU + "dlu, ");
          }
        } else {
          if (flex > 0) {
            rowsSB.append(align + ":pref:grow("
                + ((double) flex / (double) vFlex) + ")");
          } else {
            rowsSB.append(align + ":pref");
          }
        }

        final List<Element> elementsWithUI = new ArrayList<Element>();
        for (int j = 0, m = row.getElementCount(); j < m; j++) {
          final Element child = row.getElement(j);
          if (child.getUI() != null) {
            elementsWithUI.add(child);
          }
        }

        for (int j = 0, m = elementsWithUI.size(); j < m; j++) {
          final Element child = elementsWithUI.get(j);
          final Widget widget = child.getUI();

          int colspan = 1;
          if (child.getAttributeMap().containsKey("colspan")) {
            colspan = Integer.parseInt((String) child.getAttributeMap().get(
                "colspan"));
          }

          int rowspan = 1;
          if (child.getAttributeMap().containsKey("rowspan")) {
            rowspan = Integer.parseInt((String) child.getAttributeMap().get(
                "rowspan"));
          }

          layoutPanel.add(widget, CellConstraints.xywh(2 * j + 1, 2 * i + 1,
              2 * colspan - 1, 2 * rowspan - 1));

          if (j >= cols) {
            if (j + 1 < m) {
              columnsSB.append("pref, " + HDLU + "dlu, ");
            } else {
              columnsSB.append("pref");
            }
            ++cols;
          }
        }
      }
    }

    layoutPanel.setLayout(new FormLayout(columnsSB.toString(),
        rowsSB.toString()));

    return layoutPanel;
  }

  private int calculateVFlex() {
    int flex = 0;
    if (rows != null) {
      for (int i = 0, n = rows.getElementCount(); i < n; ++i) {
        Row row = (Row) rows.getElement(i);
        if (row.getAttributeMap().containsKey(Element.FLEX)) {
          flex += row.getFlex();
        }
      }
    }
    return flex;
  }

  private int calculateHFlex() {
    int flex = 0;
    if (columns != null) {
      for (int i = 0, n = columns.getElementCount(); i < n; ++i) {
        Column column = (Column) columns.getElement(i);
        if (column.getAttributeMap().containsKey(Element.FLEX)) {
          flex += column.getFlex();
        }
      }
    }
    return flex;
  }

  @Override
  protected void elementRemoved(Element element) {
    layoutPanel.remove(element.getUI());
    super.elementRemoved(element);
  }

}
